﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ARR.Entities;
using ARR.DataModel.Repositories;
using DDF.Entity;
using ARR.Services.BizRules;
using System.Transactions;
using ARR.Entities.Gateways;
using System.ServiceModel;

namespace ARR.Services.Domain
{
    public static class CategoriesDomain
    {
        public static List<Category> GetAll()
        {
            var rows = CategoriesRepository.Select();
            var categories = rows.ConvertAll<Category>(c => new Category(c));

            var rolesCache = CategoryRolesRepository.Select().ConvertAll<CategoryRole>(row => new CategoryRole(row));
            var universesCache = CategoryUniverseRepository.Select().ConvertAll <CategoryUniverse>(row => new CategoryUniverse(row));

            categories.ForEach(cat =>
                {
                    cat.Role = rolesCache.SingleOrDefault(x => x.Id == cat.CategoryRoleId);
                    cat.Universe = universesCache.SingleOrDefault(x => x.Id == cat.CategoryUniverseId);

                    cat.GetRow().ChangeState(EntityState.Original);
                    cat.ChangeState(EntityState.Original);
                });

            return categories;
        }

        public static List<Category> GetCleanCPCategories()
        {
            var rows = CategoriesRepository.Select(x => x.Level == 2 && x.ParentId == 6).OrderBy(x => x.Code).ToList(); //Traigo solo las de CLEANCP
            var categories = rows.ConvertAll<Category>(c => new Category(c));

            categories.ForEach(cat => cat.ChangeState(EntityState.Original));

            return categories;
        }

        public static Category GetById(int categoryId)
        {
            Category category = null;

            var row = CategoriesRepository.Select(cat => cat.Id == categoryId).SingleOrDefault();

            if (row != null)
            {
                category = new Category(row);

                if (category.CategoryRoleId.HasValue)
                {
                    var roleRow = CategoryRolesRepository.Select(x => x.Id == category.CategoryRoleId.Value).Single();
                    if(roleRow != null)
                        category.Role = new CategoryRole(roleRow);
                }

                if (category.CategoryUniverseId.HasValue)
                {
                    var universeRow = CategoryUniverseRepository.Select(x => x.Id == category.CategoryUniverseId.Value).Single();
                    if(universeRow != null)
                        category.Universe = new CategoryUniverse(universeRow);
                }
            }

            category.ChangeState(EntityState.Original);

            return category;
        }

        public static List<Category> GetByLevel(int level)
        {
            var rows = CategoriesRepository.Select(cat => cat.Level == level);
            var categories = rows.ConvertAll<Category>(c => new Category(c));

            categories.ForEach(cat => cat.ChangeState(EntityState.Original));

            return categories;
        }

        public static List<Category> GetByLevels(int level)
        {
            var rows = CategoriesRepository.Select(cat => cat.Level >= level);
            var categories = rows.ConvertAll<Category>(c => new Category(c));

            categories.ForEach(cat => cat.ChangeState(EntityState.Original));

            return categories;
        }

        public static List<Category> GetByParentId(int parentId)
        {
            var rows = CategoriesRepository.Select(cat => cat.ParentId == parentId);
            var categories = rows.ConvertAll<Category>(c => new Category(c));

            categories.ForEach(cat => cat.ChangeState(EntityState.Original));

            return categories;
        }

        public static List<CategoryUniverse> GetCategoryUniverses()
        {
            var rows = CategoryUniverseRepository.Select();
            var universes = rows.ConvertAll<CategoryUniverse>(row => new CategoryUniverse(row));

            universes.ForEach(uni => uni.ChangeState(EntityState.Original));

            return universes;
        }

        public static List<CategoryRole> GetCategoryRoles()
        {
            var rows = CategoryRolesRepository.Select();
            var roles = rows.ConvertAll<CategoryRole>(row => new CategoryRole(row));

            roles.ForEach(role => role.ChangeState(EntityState.Original));

            return roles;
        }

        public static List<TheoreticalMargin> GetTheoreticalMargins(int countryId)
        {
            var rows = TheoreticalMarginsRepository.Select(tm => tm.CountryId == countryId);
            var margins = rows.ConvertAll<TheoreticalMargin>(row => new TheoreticalMargin(row));

            var categoriesCache = GetAll();

            foreach (TheoreticalMargin margin in margins)
            {
                if (margin.CategoryId.HasValue)
                {
                    margin.Category = categoriesCache.SingleOrDefault(x => x.Id == margin.CategoryId.Value);
                }

                margin.ChangeState(EntityState.Original);
            }

            return margins;

        }

        public static void SaveTheoreticalMargins(List<TheoreticalMargin> margins)
        {
            using (TransactionScope scope = new TransactionScope())
            {
                AuditTrail audit = null;
                CategoryRow cat = null;
                ArticleRow art = null;

                foreach (TheoreticalMargin margin in margins)
                {
                    //CategoryMatchBizRules.ValidateSave(margin);

                    if (margin.IsValid)
                    {
                        if (margin.GetEntityState() == EntityState.New)
                        {
                            TheoreticalMarginsRepository.Insert(margin.GetRow());
                        }
                        else if (margin.GetEntityState() == EntityState.Modified)
                        {
                            TheoreticalMarginsRepository.Update(margin.GetRow());
                        }
                    }

                    string text = null;
                    if (margin.CategoryId.HasValue)
                    {
                        cat = CategoriesRepository.Select(c => c.Id == margin.CategoryId.Value).SingleOrDefault();
                        text = "la categoría " + cat.Name;
                    }
                    else if (margin.ArticleId.HasValue)
                    {
                        art = ArticlesRepository.Select(a => a.Id == margin.ArticleId.Value).SingleOrDefault();
                        text = "el artículo " + art.Name;
                    }


                    audit = new AuditTrail
                    {
                        Comments = string.Format("Se generó/modificó un margen teorico para {0} con un valor de .", text, margin.Margin),
                        Date = DateTime.Now,
                        Source = "Margenes Teoricos"
                    };

                    AuditTrailsRepository.Insert(audit.GetRow());

                }

                scope.Complete();
            }
        }

        //TODO: Unificar el save de master y importador.
        public static void SaveCategoryMatches(List<CategoryMatch> matches)
        {
            using (TransactionScope scope = new TransactionScope())
            {
                AuditTrail audit = null;
                CategoryRow cat = null;

                foreach (CategoryMatch match in matches)
                {
                    CategoryMatchBizRules.ValidateSave(match);

                    if (match.IsValid)
                    {
                        if (match.GetEntityState() == EntityState.New)
                        {
                            CategoryMatchRepository.Insert(match.GetRow());
                        }
                        else if (match.GetEntityState() == EntityState.Modified)
                        {
                            CategoryMatchRepository.Update(match.GetRow());
                        }
                    }

                    cat = CategoriesRepository.Select(c => c.Id == match.CategoryId).SingleOrDefault();

                    audit = new AuditTrail
                    {
                        Comments = string.Format("Se generó una equivalencia de categoria '{0}' contra la categoria NACS '{1}', y se actualizarón todos los articulos a la nueva categoria.", match.Name, cat.Name),
                        Date = DateTime.Now,
                        ErrorType = "CategoryMatch",
                        Source = "UI:'Importador de Articulos'"
                    };

                    AuditTrailsRepository.Insert(audit.GetRow());

                }

                scope.Complete();
            }
        }

        public static List<CategoryMatch> GetCategoryMatches(int countryId)
        {
            var rows = CategoryMatchRepository.Select(cm => cm.CountryId == countryId);
            var matches = rows.ConvertAll<CategoryMatch>(row => new CategoryMatch(row));

            var categoriesCache = GetAll();

            foreach (CategoryMatch match in matches)
            {
                if (match.SourceCategoryId.HasValue)
                {
                    match.SourceCategory = categoriesCache.SingleOrDefault(x => x.Id == match.SourceCategoryId.Value);
                }

                match.TargetCategory = categoriesCache.SingleOrDefault(x => x.Id == match.CategoryId);

                match.ChangeState(EntityState.Original);
            }

            return matches;

        }

        public static void SaveCategoryMatchesMaster(List<CategoryMatch> matches)
        {
            using (TransactionScope scope = new TransactionScope())
            {
                AuditTrail audit = null;
                string sourceCategory = null;

                foreach (CategoryMatch match in matches)
                {
                    //CategoryMatchBizRules.ValidateSave(match);

                    //TODO: Llevar esta validación a BizRules cuando se unifiquen los Save de CategoryMatch.
                    if (match.SourceCategoryId.HasValue)
                    {
                        var articles = ArticlesRepository.Select(art => art.CategoryId == match.SourceCategoryId.Value);
                        if (articles.Any())
                            throw new FaultException<ARRExceptionDetail>(
                                new ARRExceptionDetail(
                                    string.Format("No se puede guardar la equivalencia de la categoría {0} porque existen artículos relacionados a esta categoría.", match.SourceCategory.Name),
                                    ExceptionSeverity.Validation));
                    }

                    if (match.IsValid)
                    {
                        if (match.GetEntityState() == EntityState.New)
                        {
                            CategoryMatchRepository.Insert(match.GetRow());
                        }
                        else if (match.GetEntityState() == EntityState.Modified)
                        {
                            CategoryMatchRepository.Update(match.GetRow());
                        }
                    }

                    sourceCategory = match.SourceCategory != null ? match.SourceCategory.Name : match.Name;

                    audit = new AuditTrail
                    {
                        Comments = string.Format("Se generó/modificó una equivalencia de la categoría {0} hacia la categoría {1}", sourceCategory, match.TargetCategory.Name),
                        Date = DateTime.Now,
                        Source = "Equivalencia de Categorias"
                    };

                    AuditTrailsRepository.Insert(audit.GetRow());

                }

                scope.Complete();
            }
        }

        public static void SaveHierarchy(List<Category> categories)
        {
            IEnumerable<Category> categoriesLvl1 = categories.Where(x => x.GetEntityState() != EntityState.Original);

            using (TransactionScope scope = new TransactionScope())
            {
                //Level 1
                foreach (var cat1 in categoriesLvl1)
                {
                    if (cat1.GetRow().GetEntityState() == EntityState.New)
                    {
                        cat1.Level = 1;
                        CategoriesRepository.Insert(cat1.GetRow());
                    }
                    else if (cat1.GetRow().GetEntityState() == EntityState.Modified)
                    {
                        CategoriesRepository.Update(cat1.GetRow());
                    }

                    //Level 2, Only if has changes
                    foreach (var cat2 in cat1.SubCategories.Where(x => x.GetEntityState() != EntityState.Original))
                    {
                        if (cat2.GetRow().GetEntityState() == EntityState.New)
                        {
                            cat2.Level = 2;
                            cat2.ParentId = cat1.Id;
                            CategoriesRepository.Insert(cat2.GetRow());
                        }
                        else if (cat2.GetRow().GetEntityState() == EntityState.Modified)
                        {
                            CategoriesRepository.Update(cat2.GetRow());
                        }

                        //Level 3, Only if has changes
                        foreach (var cat3 in cat2.SubCategories.Where(x => x.GetEntityState() != EntityState.Original))
                        {
                            if (cat3.GetRow().GetEntityState() == EntityState.New)
                            {
                                cat3.Level = 3;
                                cat3.ParentId = cat2.Id;
                                CategoriesRepository.Insert(cat3.GetRow());
                            }
                            else if (cat3.GetRow().GetEntityState() == EntityState.Modified)
                            {
                                CategoriesRepository.Update(cat3.GetRow());
                            }
                        }
                    }
                }

                scope.Complete();
            }
        }

    }
}
